<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>医疗知识图谱可视化</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vis-network@9.1.0/dist/vis-network.min.css">
    <style>
        body {
            font-family: "Microsoft YaHei", sans-serif;
            background-color: #f5f5f7;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        .header {
            background: linear-gradient(135deg, #6a1b9a, #8e24aa);
            color: white;
            padding: 1rem 2rem;
            box-shadow: 0 2px 10px rgba(0,0,0,0.15);
        }
        .container {
            display: flex;
            height: calc(100vh - 68px);
        }
        .sidebar {
            width: 320px;
            background-color: white;
            border-right: 1px solid #e0e0e0;
            padding: 1.5rem;
            overflow-y: auto;
            display: flex;
            flex-direction: column;
        }
        .graph-container {
            flex: 1;
            position: relative;
        }
        #network {
            width: 100%;
            height: 100%;
            background-color: #fafafa;
            border-left: 1px solid #e0e0e0;
        }
        .search-box {
            margin-bottom: 1.5rem;
        }
        .entity-card {
            background-color: white;
            border-radius: 8px;
            padding: 1rem;
            margin-bottom: 0.75rem;
            box-shadow: 0 2px 5px rgba(0,0,0,0.05);
            cursor: pointer;
            transition: all 0.3s;
            border-left: 4px solid #9c27b0;
        }
        .entity-card:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
            border-left-color: #6a1b9a;
        }
        .entity-name {
            font-weight: bold;
            color: #6a1b9a;
            font-size: 1rem;
            margin-bottom: 0.25rem;
        }
        .entity-count {
            display: inline-block;
            padding: 0.2rem 0.6rem;
            border-radius: 12px;
            font-size: 0.8rem;
            background-color: #f3e5f5;
            color: #6a1b9a;
        }
        .relations-section {
            margin-top: 1.5rem;
            flex: 1;
            overflow-y: auto;
        }
        .relation-item {
            padding: 0.75rem;
            border-bottom: 1px solid #eee;
            cursor: pointer;
            transition: background-color 0.2s;
        }
        .relation-item:hover {
            background-color: #f9f5fc;
        }
        .relation-type {
            color: #e91e63;
            font-weight: 500;
            margin-right: 0.5rem;
        }
        .relation-direction {
            font-size: 0.8rem;
            color: #9e9e9e;
            margin-right: 0.5rem;
        }
        .loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            text-align: center;
            z-index: 1000;
            background: rgba(255, 255, 255, 0.9);
            padding: 2rem;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
        }
        .stats-bar {
            background-color: #f3e5f5;
            padding: 0.5rem 1rem;
            border-radius: 4px;
            margin-bottom: 1rem;
            font-size: 0.9rem;
        }
        .btn-purple {
            background-color: #6a1b9a;
            border-color: #6a1b9a;
            color: white;
        }
        .btn-purple:hover {
            background-color: #4a148c;
            border-color: #4a148c;
            color: white;
        }
    </style>
</head>
<body>
    <div class="header">
        <div class="d-flex justify-content-between align-items-center">
            <div>
                <h1 class="h3 mb-0">医疗知识图谱可视化系统</h1>
                <p class="mb-0">探索疾病、症状和患者之间的关系网络</p>
            </div>
            <div id="connection-status" class="badge bg-success">已连接</div>
        </div>
    </div>

    <div class="container">
        <div class="sidebar">
            <div class="search-box">
                <div class="input-group">
                    <input type="text" class="form-control" id="search-input"
                           placeholder="搜索实体（如：女性、摔伤）">
                    <button class="btn btn-purple" id="search-btn">
                        <i class="fa fa-search"></i>
                    </button>
                </div>
            </div>

            <div class="stats-bar d-flex justify-content-between">
                <span id="entity-count">加载中...</span>
                <span id="relation-count">加载中...</span>
            </div>

            <h5>实体列表 <small class="text-muted" id="entity-list-count"></small></h5>
            <div id="entity-list" style="flex: 1; overflow-y: auto;">
                <div class="text-center text-muted py-4">
                    <div class="spinner-border spinner-border-sm text-primary" role="status"></div>
                    <div class="mt-2">加载实体中...</div>
                </div>
            </div>

            <div class="relations-section" id="relations-section">
                <h5>关联关系 <small class="text-muted" id="selected-entity"></small></h5>
                <p class="text-muted">点击实体查看关系</p>
            </div>
        </div>

        <div class="graph-container">
            <div id="network"></div>
            <div class="loading" id="loading">
                <div class="spinner-border text-primary" role="status">
                    <span class="visually-hidden">加载中...</span>
                </div>
                <p class="mt-2">正在加载知识图谱...</p>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vis-network@9.1.0/dist/vis-network.min.js"></script>
    <script>
        // 全局变量
        let network;
        let allNodes = new vis.DataSet();
        let allEdges = new vis.DataSet();
        let isLoading = true;

        // 页面加载完成后初始化
        $(document).ready(function() {
            // 检查后端连接
            checkConnection();

            // 加载数据并初始化图谱
            loadData().then(() => {
                initGraph();
                updateStats();
                isLoading = false;
                $('#loading').hide();
            }).catch(error => {
                console.error('初始化失败:', error);
                $('#loading').html(`
                    <div class="text-danger">
                        <i class="fa fa-exclamation-triangle fa-2x"></i>
                        <p class="mt-2">知识图谱加载失败</p>
                        <button class="btn btn-sm btn-outline-primary mt-2" onclick="location.reload()">重新加载</button>
                    </div>
                `);
            });

            // 绑定搜索事件
            $('#search-btn').click(searchEntities);
            $('#search-input').keypress(function(e) {
                if (e.which === 13) searchEntities();
            });
        });

        // 检查后端连接状态
        async function checkConnection() {
            try {
                const response = await fetch('/api/health');
                const data = await response.json();
                if (data.status === 'ok') {
                    $('#connection-status').removeClass('bg-danger').addClass('bg-success').text('已连接');
                } else {
                    $('#connection-status').removeClass('bg-success').addClass('bg-danger').text('连接异常');
                }
            } catch (error) {
                console.error('连接检查失败:', error);
                $('#connection-status').removeClass('bg-success').addClass('bg-danger').text('连接失败');
            }
        }

        // 加载实体和关系数据
        async function loadData() {
            try {
                const [entitiesResp, relationsResp] = await Promise.all([
                    fetch('/api/entities').then(r => r.json()),
                    fetch('/api/relations').then(r => r.json())
                ]);

                if (entitiesResp.error) throw new Error(entitiesResp.error);
                if (relationsResp.error) throw new Error(relationsResp.error);

                // 准备节点数据
                const nodes = entitiesResp.map(entity => ({
                    id: entity.id,
                    label: entity.name.length > 10 ? entity.name.substring(0, 10) + '...' : entity.name,
                    title: entity.name,
                    value: entity.count || 1,
                    color: {
                        background: "#9c27b0",
                        border: "#7b1fa2",
                        highlight: { background: "#6a1b9a", border: "#4a148c" },
                        hover: { background: "#ab47bc", border: "#8e24aa" }
                    },
                    font: {
                        size: 14,
                        color: "white",
                        face: "Microsoft YaHei"
                    },
                    shape: 'dot',
                    borderWidth: 2,
                    shadow: true
                }));

                // 准备边数据
                const edges = relationsResp.map(rel => ({
                    from: rel.source,
                    to: rel.target,
                    label: rel.type,
                    title: rel.text || rel.type,
                    color: {
                        color: "#ce93d8",
                        highlight: "#ba68c8",
                        hover: "#e1bee7",
                        opacity: 0.8
                    },
                    font: {
                        size: 11,
                        color: "#4a148c",
                        face: "Microsoft YaHei",
                        strokeWidth: 3,
                        strokeColor: "rgba(255,255,255,0.7)"
                    },
                    arrows: "to",
                    width: 1.5,
                    smooth: { enabled: true, type: "dynamic" }
                }));

                allNodes = new vis.DataSet(nodes);
                allEdges = new vis.DataSet(edges);

                return true;
            } catch (error) {
                console.error('数据加载失败:', error);
                throw error;
            }
        }

        // 初始化图谱
        function initGraph() {
            const container = document.getElementById('network');
            const data = { nodes: allNodes, edges: allEdges };

            // 图谱配置
            const options = {
                physics: {
                    enabled: true,
                    stabilization: { iterations: 100 },
                    solver: "forceAtlas2Based",
                    forceAtlas2Based: {
                        gravitationalConstant: -100,
                        centralGravity: 0.01,
                        springLength: 200,
                        springConstant: 0.05,
                        damping: 0.4
                    }
                },
                interaction: {
                    hover: true,
                    tooltipDelay: 200,
                    zoomView: true,
                    dragView: true,
                    selectable: true
                },
                nodes: {
                    scaling: {
                        min: 15,
                        max: 40
                    }
                },
                edges: {
                    chosen: {
                        edge: function(values, id, selected, hovering) {
                            values.width = selected || hovering ? 3 : 1.5;
                        }
                    }
                },
                layout: {
                    improvedLayout: true
                },
                groups: {
                    ENTITY: {
                        shape: 'dot'
                    }
                }
            };

            // 创建网络
            network = new vis.Network(container, data, options);

            // 节点点击事件
            network.on('click', function(params) {
                if (params.nodes.length > 0) {
                    const entityId = params.nodes[0];
                    showEntityRelations(entityId);

                    // 高亮相关节点和边
                    const connectedEdges = network.getConnectedEdges(entityId);
                    const connectedNodes = [];

                    connectedEdges.forEach(edgeId => {
                        const edge = allEdges.get(edgeId);
                        if (edge.from === entityId) connectedNodes.push(edge.to);
                        if (edge.to === entityId) connectedNodes.push(edge.from);
                    });

                    // 高亮相关元素
                    network.selectNodes([entityId, ...connectedNodes]);
                    network.selectEdges(connectedEdges);
                } else {
                    // 点击空白处清除选择
                    network.unselectAll();
                    $('#relations-section').html('<h5>关联关系</h5><p class="text-muted">点击实体查看关系</p>');
                    $('#selected-entity').text('');
                }
            });

            // 双击事件 - 聚焦节点
            network.on('doubleClick', function(params) {
                if (params.nodes.length > 0) {
                    network.focus(params.nodes[0], {
                        scale: 1.2,
                        animation: { duration: 1000, easingFunction: 'easeInOutQuad' }
                    });
                }
            });

            // 初始化实体列表
            renderEntityList(allNodes.get());
        }

        // 渲染实体列表
        function renderEntityList(entities) {
            const listContainer = $('#entity-list');
            listContainer.empty();

            if (entities.length === 0) {
                listContainer.html('<div class="text-center text-muted py-4">无实体数据</div>');
                return;
            }

            // 按连接数排序
            entities.sort((a, b) => b.value - a.value);

            // 只显示前100个实体
            const displayEntities = entities.slice(0, 100);

            displayEntities.forEach(entity => {
                const card = $(`
                    <div class="entity-card" data-id="${entity.id}">
                        <div class="entity-name" title="${entity.title}">${entity.title}</div>
                        <div>
                            <span class="entity-count">${entity.value} 连接</span>
                        </div>
                    </div>
                `);

                card.click(function() {
                    const entityId = $(this).data('id');
                    showEntityRelations(entityId);

                    // 聚焦节点
                    network.focus(entityId, {
                        scale: 1.2,
                        animation: { duration: 800, easingFunction: 'easeInOutQuad' }
                    });

                    // 高亮节点
                    network.selectNodes([entityId]);
                });

                listContainer.append(card);
            });

            $('#entity-list-count').text(`(${displayEntities.length} 个)`);
        }

        // 显示实体关联关系
        async function showEntityRelations(entityId) {
            try {
                const response = await fetch(`/api/entity/${encodeURIComponent(entityId)}`);
                const data = await response.json();

                if (data.error) {
                    $('#relations-section').html(`
                        <h5>关联关系</h5>
                        <div class="alert alert-warning">${data.error}</div>
                    `);
                    return;
                }

                const entity = data.entity;
                const relations = data.relations;

                // 更新关系展示区
                const relSection = $('#relations-section');
                relSection.html(`
                    <h5>关联关系 <small class="text-muted">${entity.name}</small></h5>
                    ${relations.length > 0 ? '' : '<p class="text-muted">无关联关系</p>'}
                `);

                $('#selected-entity').text(entity.name);

                // 添加关系项
                relations.forEach(rel => {
                    const relItem = $(`
                        <div class="relation-item" data-target="${rel.target}">
                            <div>
                                <span class="relation-direction">
                                    ${rel.direction === 'OUTGOING' ? '→' : '←'}
                                </span>
                                <span class="relation-type">${rel.type}</span>
                                <span class="entity-name">${rel.target}</span>
                            </div>
                        </div>
                    `);

                    relItem.click(function() {
                        const targetId = $(this).data('target');
                        showEntityRelations(targetId);

                        // 聚焦目标节点
                        network.focus(targetId, {
                            scale: 1.2,
                            animation: { duration: 800, easingFunction: 'easeInOutQuad' }
                        });

                        // 高亮目标节点
                        network.selectNodes([targetId]);
                    });

                    relSection.append(relItem);
                });
            } catch (error) {
                console.error('获取实体关系失败:', error);
                $('#relations-section').html(`
                    <h5>关联关系</h5>
                    <div class="alert alert-danger">加载关系失败</div>
                `);
            }
        }

        // 更新统计信息
        function updateStats() {
            $('#entity-count').text(`实体: ${allNodes.length} 个`);
            $('#relation-count').text(`关系: ${allEdges.length} 个`);
        }

        // 搜索实体
        async function searchEntities() {
            const keyword = $('#search-input').val().trim();
            if (!keyword) {
                renderEntityList(allNodes.get());
                return;
            }

            try {
                const response = await fetch(`/api/search?q=${encodeURIComponent(keyword)}`);
                const results = await response.json();

                if (results.error) {
                    alert('搜索失败: ' + results.error);
                    return;
                }

                // 获取完整的节点数据
                const matchedNodes = results.map(result => {
                    const node = allNodes.get(result.id);
                    return node ? {...node, value: result.count} : null;
                }).filter(node => node !== null);

                renderEntityList(matchedNodes);

                if (matchedNodes.length > 0) {
                    // 聚焦第一个匹配的节点
                    network.focus(matchedNodes[0].id, {
                        scale: 1.2,
                        animation: { duration: 800, easingFunction: 'easeInOutQuad' }
                    });

                    // 显示第一个匹配节点的关系
                    showEntityRelations(matchedNodes[0].id);
                }
            } catch (error) {
                console.error('搜索失败:', error);
                alert('搜索失败，请检查网络连接');
            }
        }
    </script>
</body>
</html>